引子


需求: 多个课程表都需要关联到价格表

1. 设计方式一


2. 设计方式二


3. 设计方式三(最终的设计模式)


ContentType


1. 什么时候使用 ContentType

  • 当一张表需要和多张表建立外键关系的时候

2. ContentType 的说明

  • ContentType 的设计模式实际上就是引子中的设计方式三

  • ContentType 所使用的表名表就是在执行 migrate 后自动创建 django_content_type 表(存放着都是表名,当有新的表被创建时,会自动添加表名数据)



3. ContentType 的使用

  • 表的创建

    • content_type、 object_id、 content_object 字段属性名是固定写法不能随意改变

# models.py

from django.db import models
from django.contrib.contenttypes.models import ContentType  # 导入 ContentType 表(即: django_content_type)
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation


class VIPCourse(models.Model):
 """
    VIP课程
    """
    title = models.CharField(max_length=32, verbose_name='VIP课程名称')

    coupon_policy = GenericRelation('Coupon')  # 不会在数据库中生产字段,只用于反向查询数据,传入使用了 ContentType 的表名


class BasisCourse(models.Model):
"""
    基础课程
    """
    title = models.CharField(max_length=32, verbose_name='基础课程名称')

    coupon_policy = GenericRelation('Coupon')  # 不会在数据库中生产字段,只用于反向查询数据,传入使用了 ContentType 的表名


class Coupon(models.Model):
 """
    优惠券
    """
    content_type = models.ForeignKey(ContentType)  # 关联表名表(即: django_content_type),content_type 是固定字段属性名,不能随意改变
    object_id = models.PositiveIntegerField()  # 正整数类型,存放需要关联的课程 id,object_id 是固定字段属性名,不能随意改变
    content_object = GenericForeignKey('content_type', 'object_id')  # 不会在数据库中生产字段,只用于添加数据和正向查询数据,content_object 是固定字段属性名,不能随意改变

    title = models.CharField(max_length=32, verbose_name='优惠券名称')

3. 添加数据

  • 方式一

from django.contrib.contenttypes.models import ContentType  # 导入 ContentType 表(即: django_content_type)
from app01.models import *

Coupon.objects.create(
    title='vip课程优惠券一',
    content_type=ContentType.objects.filter(model='vipcourse').first(),  # 查询到的ContentType表名数据对象
    object_id=1  # 课程id
)

Coupon.objects.create(
    title='基础课程优惠券一',
    content_type=ContentType.objects.filter(model='basiscourse').first(),  # 查询到的ContentType表名数据对象
    object_id=2  # 课程id
)

  • 方式二 -> 通过 GenericForeignKey 表类方法所创建 content_object 字段进行添加 -> 推荐使用

    • 语法: content_object = 查询到的数据对象

    • 原理: 因为可以通过查询的对象获取当前数据的id和表名,然后 ContentType 组件帮你进行添加

from django.contrib.contenttypes.models import ContentType  # 导入 ContentType 表(即: django_content_type)
from app01.models import *

Coupon.objects.create(
    title='vip课程优惠券一',
content_object=VIPCourse.objects.filter(pk=2).first()  # 通过 GenericForeignKey 表类方法所创建 content_object 字段进行添加,传入查询到的数据对象
)

4. 正向查询

  • 通过定义了相关ContentType字段属性表的数据,查询出与该条数据相关的表的数据

  • 通过 GenericForeignKey 表类方法所创建 content_object 字段进行正向查询

# 通过优惠券表的数据查询出与该优惠券数据相关的课程数据

from app01.models import *

coupon_obj = Coupon.objects.filter(pk=5).first()

course_obj = coupon_obj.content_object  # VIPCourse object
course_title = coupon_obj.content_object.title  # Vue项目课程

5. 反向查询

  • 通过数据对象查询出与该数据对象相关的所有定义了相关ContentType字段属性表的数据

  • 通过 GenericRelation 表类方法所创建字段进行反向查询

# 通过课程数据查询出该课程的所有优惠券数据

from app01.models import *

vip_course_obj = VIPCourse.objects.filter(pk=2).first()

coupon_list = vip_course_obj.coupon_policy.all()  # <QuerySet [<Coupon: Coupon object>, <Coupon: Coupon object>, <Coupon: Coupon object>]>

for coupon in coupon_list:
    print(coupon.title)  # vip课程优惠券一